var Crafty = require("../core/core.js");

Crafty.extend({
    /**@
     * #Crafty.device
     * @category Misc
     * @kind Property
     *
     * Methods relating to devices such as tablets or phones
     */
    device: {
        _deviceOrientationCallback: false,
        _deviceMotionCallback: false,

        /**
         * The HTML5 DeviceOrientation event returns three pieces of data:
         *  * alpha the direction the device is facing according to the compass
         *  * beta the angle in degrees the device is tilted front-to-back
         *  * gamma the angle in degrees the device is tilted left-to-right.
         *  * The angles values increase as you tilt the device to the right or towards you.
         *
         * Since Firefox uses the MozOrientationEvent which returns similar data but
         * using different parameters and a different measurement system, we want to
         * normalize that before we pass it to our _deviceOrientationCallback function.
         *
         * @param eventData HTML5 DeviceOrientation event
         */
        _normalizeDeviceOrientation: function(eventData) {
            var data;
            if (window.DeviceOrientationEvent) {
                data = {
                    // gamma is the left-to-right tilt in degrees, where right is positive
                    tiltLR: eventData.gamma,
                    // beta is the front-to-back tilt in degrees, where front is positive
                    tiltFB: eventData.beta,
                    // alpha is the compass direction the device is facing in degrees
                    dir: eventData.alpha,
                    // deviceorientation does not provide this data
                    motUD: null
                };
            } else if (window.OrientationEvent) {
                data = {
                    // x is the left-to-right tilt from -1 to +1, so we need to convert to degrees
                    tiltLR: eventData.x * 90,
                    // y is the front-to-back tilt from -1 to +1, so we need to convert to degrees
                    // We also need to invert the value so tilting the device towards us (forward)
                    // results in a positive value.
                    tiltFB: eventData.y * -90,
                    // MozOrientation does not provide this data
                    dir: null,
                    // z is the vertical acceleration of the device
                    motUD: eventData.z
                };
            }

            Crafty.device._deviceOrientationCallback(data);
        },

        /**
         * @param eventData HTML5 DeviceMotion event
         */
        _normalizeDeviceMotion: function(eventData) {
            var acceleration = eventData.accelerationIncludingGravity,
                facingUp = acceleration.z > 0 ? +1 : -1;

            var data = {
                // Grab the acceleration including gravity from the results
                acceleration: acceleration,
                rawAcceleration:
                    "[" +
                    Math.round(acceleration.x) +
                    ", " +
                    Math.round(acceleration.y) +
                    ", " +
                    Math.round(acceleration.z) +
                    "]",
                // Z is the acceleration in the Z axis, and if the device is facing up or down
                facingUp: facingUp,
                // Convert the value from acceleration to degrees acceleration.x|y is the
                // acceleration according to gravity, we'll assume we're on Earth and divide
                // by 9.81 (earth gravity) to get a percentage value, and then multiply that
                // by 90 to convert to degrees.
                tiltLR: Math.round((acceleration.x / 9.81) * -90),
                tiltFB: Math.round(
                    ((acceleration.y + 9.81) / 9.81) * 90 * facingUp
                )
            };

            Crafty.device._deviceMotionCallback(data);
        },

        /**@
         * #Crafty.device.deviceOrientation
         * @comp Crafty.device
         * @kind Method
         *
         * @sign public Crafty.device.deviceOrientation(Function callback)
         * @param callback - Callback method executed once as soon as device orientation is change
         *
         * Do something with normalized device orientation data:
         * ~~~
         * {
         *   tiltLR    :   'gamma -- the angle in degrees the device is tilted left-to-right.',
         *   tiltFB    :   'beta -- the angle in degrees the device is tilted front-to-back',
         *   dir       :   'alpha -- the direction the device is facing according to the compass',
         *   motUD     :   'The angle's values increase as you tilt the device to the right or towards you.'
         * }
         * ~~~
         *
         * @example
         * ~~~
         * // Get DeviceOrientation event normalized data.
         * Crafty.device.deviceOrientation(function(data){
         *     Crafty.log('data.tiltLR : '+Math.round(data.tiltLR)+', data.tiltFB : '+Math.round(data.tiltFB)+', data.dir : '+Math.round(data.dir)+', data.motUD : '+data.motUD+'');
         * });
         * ~~~
         *
         * See browser support at http://caniuse.com/#search=device orientation.
         */
        deviceOrientation: function(func) {
            this._deviceOrientationCallback = func;
            if (Crafty.support.deviceorientation) {
                if (window.DeviceOrientationEvent) {
                    // Listen for the deviceorientation event and handle DeviceOrientationEvent object
                    Crafty.addEvent(
                        this,
                        window,
                        "deviceorientation",
                        this._normalizeDeviceOrientation
                    );
                } else if (window.OrientationEvent) {
                    // Listen for the MozOrientation event and handle OrientationData object
                    Crafty.addEvent(
                        this,
                        window,
                        "MozOrientation",
                        this._normalizeDeviceOrientation
                    );
                }
            }
        },

        /**@
         * #Crafty.device.deviceMotion
         * @comp Crafty.device
         * @kind Method
         *
         * @sign public Crafty.device.deviceMotion(Function callback)
         * @param callback - Callback method executed once as soon as device motion is change
         *
         * Do something with normalized device motion data:
         * ~~~
         * {
         *     acceleration : 'Grab the acceleration including gravity from the results',
         *     rawAcceleration : 'Display the raw acceleration data',
         *     facingUp : 'Z is the acceleration in the Z axis, and if the device is facing up or down',
         *     tiltLR : 'Convert the value from acceleration to degrees. acceleration.x is the acceleration according to gravity, we'll assume we're on Earth and divide by 9.81 (earth gravity) to get a percentage value, and then multiply that by 90 to convert to degrees.',
         *     tiltFB : 'Convert the value from acceleration to degrees.'
         * }
         * ~~~
         *
         * @example
         * ~~~
         * // Get DeviceMotion event normalized data.
         * Crafty.device.deviceMotion(function(data){
         *     Crafty.log('data.moAccel : '+data.rawAcceleration+', data.moCalcTiltLR : '+Math.round(data.tiltLR)+', data.moCalcTiltFB : '+Math.round(data.tiltFB)+'');
         * });
         * ~~~
         *
         * See browser support at http://caniuse.com/#search=motion.
         */
        deviceMotion: function(func) {
            this._deviceMotionCallback = func;
            if (Crafty.support.devicemotion) {
                if (window.DeviceMotionEvent) {
                    // Listen for the devicemotion event and handle DeviceMotionEvent object
                    Crafty.addEvent(
                        this,
                        window,
                        "devicemotion",
                        this._normalizeDeviceMotion
                    );
                }
            }
        }
    }
});
